package edu.joe.game;

import java.io.InputStream;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GL2ES1;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.fixedfunc.GLLightingFunc;
import javax.media.opengl.fixedfunc.GLMatrixFunc;
import javax.media.opengl.glu.GLU;

import com.jogamp.opengl.util.texture.Texture;
import com.jogamp.opengl.util.texture.TextureIO;

import edu.joe.game.premade.PresetGroup;
import edu.joe.game.solvers.NBodySolver;

/**
 * Particle scene, contains the particle source, loads the texture (todo, move
 * to source instantiation, or create a map of textures), maintains camera
 * rotation (todo, move to some sort of camera manager), also has the last time
 * and accumulator time for calculation of dt
 * 
 * @author Joe
 * 
 */
public class ParticleScene implements GLEventListener {
	private double lastTime;
	private double accumTime;
	GLU glu;

	private ParticleSource testSource;
	private SceneCamera camera;

	private Texture particleTexture;

	/**
	 * Display logic here
	 */
	@Override
	public void display(GLAutoDrawable drawable) {
		update();
		render(drawable);
	}

	@Override
	public void dispose(GLAutoDrawable arg0) {
		// TODO Auto-generated method stub

	}

	/**
	 * Initialization logic. Most of this is boilerplate stuff from Nehe
	 * http://nehe.gamedev.net/tutorial/creating_an_opengl_window_(win32)/13001/
	 */
	@Override
	public void init(GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2();
		gl.glShadeModel(GLLightingFunc.GL_SMOOTH);
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		gl.glClearDepth(1.0);
		gl.glEnable(GL.GL_DEPTH_TEST);
		gl.glDepthFunc(GL.GL_LEQUAL);
		gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
		gl.glLoadIdentity();
		gl.glHint(GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
		gl.glEnable(GL.GL_TEXTURE_2D);
		glu = GLU.createGLU();

		PresetGroup particle = PresetGroup.makeWater();

		// Boilerplate texture loading code
		// http://gpsnippets.blogspot.com/2009/09/drawing-html-with-jogl-part-1-textures.html
		InputStream textureStr = ClassLoader
				.getSystemResourceAsStream(particle.graphics.texture);
		if (textureStr == null) {
			System.err.println("Unable to load " + particle.graphics.texture
					+ ", is the assets folder on your classpath?");
			System.exit(1);
		}
		particleTexture = null;
		try {
			particleTexture = TextureIO.newTexture(textureStr, false, "png");
		} catch (Exception e) {
			e.printStackTrace();
		}
		gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE,
				GL2.GL_MODULATE);

		particleTexture
				.setTexParameteri(gl, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
		particleTexture
				.setTexParameteri(gl, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
		particleTexture.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER,
				GL.GL_LINEAR);
		particleTexture.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER,
				GL.GL_LINEAR);

		particleTexture.bind(gl);
		this.camera = new SceneCamera(0, 0, 0, 0, 2, 0);
		this.camera.orbitSpeed = 0.2;
		testSource = new ParticleSource(particle.emitter, particle.physics,
				particle.graphics);
		this.accumTime = 0;
	}

	@Override
	public void reshape(GLAutoDrawable drawable, int arg1, int arg2, int width,
			int height) {
		GL2 gl = drawable.getGL().getGL2();
		gl.glViewport(0, 0, width, height);
		gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
		gl.glLoadIdentity();
		GLU glu = GLU.createGLU();
		glu.gluPerspective(60, 1.0 * (float) width / height, 0.1f, 100f);
		gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
	}

	/**
	 * Update objects in the scene. Camera gets orbited, particle source gets
	 * updated
	 */
	private void update() {
		double dt = (System.currentTimeMillis() - this.lastTime) * 0.001;
		this.lastTime = System.currentTimeMillis();
		this.accumTime += dt;
		camera.orbitDistance = Math
				.cos(this.accumTime * this.camera.orbitSpeed) * 3 + 10;
		testSource.update(dt);
		camera.setCam(Math.cos(this.accumTime * this.camera.orbitSpeed)
				* camera.orbitDistance, 3,
				Math.sin(this.accumTime * this.camera.orbitSpeed)
						* camera.orbitDistance);

	}

	/**
	 * Render objects in scene. Camera gets lookat'd first, then this matrix is
	 * pushed to save recalculating it every time we translate or rotate. Floor
	 * is drawn, and then the particle source is rendered.
	 * 
	 * @param drawable
	 */
	private void render(GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2();
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		gl.glLoadIdentity();

		glu.gluLookAt(camera.camX, camera.camY, camera.camZ, camera.atX,
				camera.atY, camera.atZ, 0, 1, 0);
		gl.glPushMatrix();

		drawFloor(gl);
		testSource.render(gl, camera);
	}

	private void drawFloor(GL2 gl) {
		gl.glBegin(GL2.GL_LINE_LOOP); // Drawing Using Triangles
		gl.glColor3f(0.3f, 0.3f, 0.3f);
		gl.glVertex3f(-10.0f, 0.0f, 10.0f); // Top
		gl.glColor3f(0.3f, 0.3f, 0.3f);
		gl.glVertex3f(10.0f, 0.0f, 10.0f); // Bottom Left
		gl.glColor3f(0.3f, 0.3f, 0.3f);
		gl.glVertex3f(10.0f, 0.0f, -10.0f); // Bottom Right
		gl.glColor3f(0.3f, 0.3f, 0.3f);
		gl.glVertex3f(-10.0f, 0.0f, -10.0f); // Bottom Right
		gl.glEnd();
	}

}
